package model

import (
	"errors"
	"net/http"
	"strings"
	"time"

	"github.com/gin-gonic/gin"

	"gitee.com/goframe/lt-collector/libs/libgin"
	"gitee.com/goframe/lt-collector/libs/orma"
	"gitee.com/goframe/lt-collector/libs/utils"
)

const (
	_ = iota
	AuthManager
	AuthUser
)

var (
	secretKey = []byte("KEMyVbCbj8cFdvxNtOXL9eStjsAuHc7aYT9tLeboHFR58pqt")

	ErrUserName         = errors.New("用户不存在")
	ErrUserNameModified = errors.New("用户名重复")
	ErrPassword         = errors.New("密码错误")
	ErrForbidden        = errors.New("未授权的链接")
	ErrSecret           = errors.New("身份验校失败")
)

type (
	Auth struct{}
	User struct {
		orma.BaseModel `xorm:"extends"`

		Name     string `json:"name" xorm:"varchar(50) notnull unique comment(用户名)"`
		Password string `json:"password,omitempty" xorm:"varchar(50) notnull comment(密码)"`
		Role     int    `json:"role" xorm:"notnull index comment(用户权限)"`
		//Secret 由uid created 加密生成,用于验证用户身份
		Secret string `json:"secret" xorm:"TEXT notnull comment(静态秘钥)"`
	}

	SecretBind struct {
		Uid int64
		//秘钥生成时间,纳秒时间戳
		Generated int64
	}
)

//ChangePassword 修改密码
func (a Auth) ChangePassword() gin.HandlerFunc {
	type Bind struct {
		OldPwd string `json:"old_pwd"`
		NewPwd string `json:"new_pwd"`
	}
	return libgin.Return.WarpHandler(func(c *gin.Context) (err error) {
		bindBean := new(Bind)
		if err = c.ShouldBind(bindBean); err != nil {
			return
		}
		user := c.MustGet("User").(*User)
		if err = user.VerifyPassword(bindBean.OldPwd); err != nil {
			return
		}
		user.SetPassword(bindBean.NewPwd)
		if err = user.generateSecret(); err != nil {
			return
		}
		err = orma.NormalUpdate(user.Session().ID(user.ID).Update(user))
		if err != nil {
			return
		}
		libgin.Return.GinOK(c, user.Secret)
		return
	})
}

//ResetPassword 重置密码
func (a Auth) ResetPassword() gin.HandlerFunc {
	return libgin.Return.WarpHandler(func(c *gin.Context) (err error) {
		uid := c.Param("uid")
		user := new(User)
		err = orma.NormalGet(user.Session().ID(uid).Get(user))
		if err != nil {
			return
		}
		user.SetPassword("123456")
		if err = user.generateSecret(); err != nil {
			return
		}
		err = orma.NormalUpdate(user.Session().ID(user.ID).Update(user))
		if err != nil {
			return
		}
		libgin.Return.GinOK(c, user.Secret)
		return
	})
}

//AddUser 添加用户
func (a Auth) AddUser() gin.HandlerFunc {
	type Bind struct {
		Username string `json:"username"`
	}
	return libgin.Return.WarpHandler(func(c *gin.Context) (err error) {
		bindBean := new(Bind)
		if err = c.ShouldBind(bindBean); err != nil {
			return
		}
		user := &User{
			Name:     bindBean.Username,
			Password: "123456",
			Role:     AuthUser,
		}
		has, e := user.Session().Where("`name`=?", bindBean.Username).NoAutoCondition().Exist(user)
		if e != nil {
			err = e
			return
		}
		if has {
			return ErrUserNameModified
		}
		err = orma.NormalInsert(user.Session().InsertOne(user))
		if err != nil {
			return
		}
		libgin.Return.GinOK(c, user.Secret)
		return
	})
}

//Login 登录
func (a Auth) Login() gin.HandlerFunc {
	type LoginBean struct {
		Username string `json:"username"`
		Password string `json:"password"`
	}
	return libgin.Return.WarpHandler(func(c *gin.Context) (err error) {
		bindBean := new(LoginBean)
		if err = c.ShouldBind(bindBean); err != nil {
			return
		}
		loginUser := new(User)
		err = orma.NormalGet(loginUser.Session().Where("`name`=?", bindBean.Username).Get(loginUser))
		if err != nil {
			if err == orma.ErrOrmNotFound {
				return ErrUserName
			}
			return
		}
		if err = loginUser.VerifyPassword(bindBean.Password); err != nil {
			return
		}
		libgin.Return.GinOK(c, loginUser.Secret)
		return
	})
}

func (m *User) AfterInsert() {
	err := orma.NormalGet(m.Session().Where("name=?", m.Name).NoAutoCondition().Get(m))
	if err != nil {
		return
	}
	err = m.generateSecret()
	if err != nil {
		return
	}
	_ = orma.NormalUpdate(m.Session().ID(m.ID).Cols("secret").Update(m))
}

func (m *User) PasswordFieldName() string {
	return "password"
}

func (m *User) SetPassword(pwd string) {
	m.Password = utils.GeneratePassword(pwd)
}

func (m *User) VerifyPassword(plaintext string) error {
	if utils.VerifyPassword(plaintext, m.Password) {
		return nil
	}
	return ErrPassword
}

func (m *User) BeforeInsert() {
	m.SetPassword(m.Password)
}

func (m *User) InitData() interface{} {
	return []*User{{
		Name:     "admin",
		Password: "123456",
		Role:     AuthManager,
	}}
}

func (m *User) getById(uid int64) error {
	m.ID = uid
	return BindIDModel(m)
}

func (m *User) AfterDelete() {
	deleteCacheID(m)
}

func (m *User) AfterUpdate() {
	_ = ResetCacheID(m)
}

//Manager 管理员身份过滤
func (a Auth) Manager(c *gin.Context) (err error) {
	user := c.MustGet("User").(*User)
	if user.Role != AuthManager {
		c.Abort()
		libgin.Return.GinSet(c, http.StatusUnauthorized, "权限不足")
	}
	return
}

//Auth 所有用户身份过滤器
func (a Auth) Auth(c *gin.Context) (err error) {
	var (
		secret string

		user *User
	)
	defer func() {
		if err != nil {
			c.Abort()
			libgin.Return.GinSet(c, http.StatusUnauthorized, err.Error())
			err = nil
		}
	}()
	secret = strings.Replace(c.GetHeader("Authorization"), "Bearer ", "", 1)
	switch len(secret) {
	case 0:
		secret = c.Query("secret")
		fallthrough
	default:
		if len(secret) > 0 {
			break
		}
		err = ErrForbidden
		return
	}
	user, err = a.getUser(secret)
	if err != nil {
		return
	}
	c.Set("User", user)
	return
}

func (a Auth) getUser(secret string) (user *User, err error) {
	sBind := new(SecretBind)
	if err = sBind.decryptSecret(secret); err != nil {
		return
	}
	user = new(User)
	if err = user.getById(sBind.Uid); err != nil {
		return
	}
	if secret != user.Secret {
		err = ErrSecret
	}
	return
}

//AuthBySecret ...
func (m *User) AuthBySecret(secret string) (err error) {
	sBind := new(SecretBind)
	if err = sBind.decryptSecret(secret); err != nil {
		return
	}
	if err = m.getById(sBind.Uid); err != nil {
		return
	}
	if secret != m.Secret {
		err = ErrSecret
	}
	return
}

//generateSecret 生成秘钥
func (m *User) generateSecret() (err error) {
	sBind := new(SecretBind)
	m.Secret, err = sBind.generateSecret(m.ID)
	return
}

//generateSecret get a secret by uid
func (s *SecretBind) generateSecret(uid int64) (secret string, err error) {
	s.Uid = uid
	s.Generated = time.Now().UnixNano()
	var jsonString string
	jsonString, err = json.MarshalToString(s)
	if err != nil {
		return
	}
	secret = string(utils.AESEncrypt(secretKey[:32], []byte(jsonString)))
	secret = strings.ToUpper(secret)
	return
}

//decryptSecret decrypt a secret string to pointer
func (s *SecretBind) decryptSecret(secret string) (err error) {
	var (
		plainBytes []byte
	)
	plainBytes, err = utils.AESDecrypt(secretKey[:32], []byte(strings.ToLower(secret)))
	if err != nil {
		err = ErrSecret
		return
	}
	err = json.Unmarshal(plainBytes, s)
	return
}
